package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"

	"github.com/xlcetc/cryptogm/sm/sm3"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for managing an Asset
type SmartContract struct {
	contractapi.Contract
}

type MarriageInfo struct {
	MarriageID string `json:"marriage_id"`
	Name1      string `json:"name1"`
	Name2      string `json:"name2"`
	TimeStamp  int64  `json:"timestamp"`
	Desc       string `json:"desc"`
}

type Marriage2Person struct {
	MarriageID string `json:"marriage_id"`
	PersonName string `json:"person_name"`
}

func (s *SmartContract) Register(
	ctx contractapi.TransactionContextInterface,
	name, cid string) error {

	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		return fmt.Errorf("failed to get MSPID: %v", err)
	}
	if clientMSPID != "Org1MSP" {
		return fmt.Errorf("client is not authorized to Register user")
	}

	data, err := ctx.GetStub().GetState(cid)
	if err != nil {
		return fmt.Errorf("failed to get state:%v", err)
	}
	if data != nil {
		return fmt.Errorf("user already exists")
	}

	mp := Marriage2Person{"", name}
	data, err = json.Marshal(mp)
	if err != nil {
		return err
	}

	err = ctx.GetStub().PutState(cid, data)
	if err != nil {
		return fmt.Errorf("failed to put to world state. %v", err)
	}

	return nil

}

func (s *SmartContract) Create(
	ctx contractapi.TransactionContextInterface,
	cid1, cid2 string) error {

	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		return fmt.Errorf("failed to get MSPID: %v", err)
	}
	if clientMSPID != "Org1MSP" {
		return fmt.Errorf("client is not authorized to Register user")
	}

	data1, err := ctx.GetStub().GetState(cid1)
	if err != nil {
		return err
	}
	if data1 == nil {
		return fmt.Errorf("user does not register")
	}

	var mp1 Marriage2Person

	err = json.Unmarshal(data1, &mp1)
	if err != nil {
		return err
	}

	if mp1.MarriageID != "" {
		return fmt.Errorf("user %s alread married", mp1.PersonName)
	}

	data2, err := ctx.GetStub().GetState(cid2)
	if err != nil {
		return err
	}
	if data2 == nil {
		return fmt.Errorf("user does not register")
	}

	var mp2 Marriage2Person

	err = json.Unmarshal(data2, &mp2)
	if err != nil {
		return err
	}

	if mp2.MarriageID != "" {
		return fmt.Errorf("user %s alread married", mp2.PersonName)
	}

	var marriageinfo MarriageInfo
	marriageinfo.Name1 = mp1.PersonName
	marriageinfo.Name2 = mp2.PersonName
	marriageinfo.MarriageID = makemarriageid(cid1, cid2)
	timestamp, _ := ctx.GetStub().GetTxTimestamp()
	marriageinfo.TimeStamp = timestamp.GetSeconds()
	marriageinfo.Desc = "love for ever"

	marriageJson, err := json.Marshal(marriageinfo)
	if err != nil {
		return err
	}
	err = ctx.GetStub().PutState(marriageinfo.MarriageID, marriageJson)
	if err != nil {
		return fmt.Errorf("failed to put to world state. %v", err)
	}

	mp1.MarriageID = marriageinfo.MarriageID
	mp1Json, err := json.Marshal(mp1)
	if err != nil {
		return err
	}
	err = ctx.GetStub().PutState(cid1, mp1Json)
	if err != nil {
		return fmt.Errorf("failed to put to world state. %v", err)
	}

	mp2.MarriageID = marriageinfo.MarriageID
	mp2Json, err := json.Marshal(mp2)
	if err != nil {
		return err
	}
	err = ctx.GetStub().PutState(cid2, mp2Json)
	if err != nil {
		return fmt.Errorf("failed to put to world state. %v", err)
	}

	return nil

}

func makemarriageid(cid1, cid2 string) string {

	// 使用区块链的时间戳

	data := bytes.Join([][]byte{[]byte(cid1),
		[]byte(cid2),
	}, []byte{})
	return fmt.Sprintf("%x", sm3.SumSM3(data))
}

func (s *SmartContract) Query(
	ctx contractapi.TransactionContextInterface,
	cid string) (*MarriageInfo, error) {
	userJson, err := ctx.GetStub().GetState(cid)
	if err != nil {
		return nil, fmt.Errorf("failed to read from world state: %v", err)
	}
	if userJson == nil {
		return nil, fmt.Errorf("the user %s does not exist", cid)
	}

	var mp Marriage2Person
	err = json.Unmarshal(userJson, &mp)
	if err != nil {
		return nil, err
	}

	marriageJson, err := ctx.GetStub().GetState(mp.MarriageID)
	if err != nil {
		return nil, err
	}

	var marriageinfo MarriageInfo

	err = json.Unmarshal(marriageJson, &marriageinfo)
	if err != nil {
		return nil, err
	}

	return &marriageinfo, nil

}

func main() {
	chaincode, err := contractapi.NewChaincode(&SmartContract{})
	if err != nil {
		log.Panicf("Error creating user chaincode: %v", err)
	}

	if err := chaincode.Start(); err != nil {
		log.Panicf("Error starting user chaincode: %v", err)
	}
}
